from random import sample

from django.db.models import Q
from django.shortcuts import render

# Create your views here.
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticated  #登陆的权限控制
from work.models import Label, Topic
from work.serializers import LabelSerializer, TopicSerializer,TopicStudentSerializer
from utils.permission import *
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination  #分页功能，前面是每一页多少条数据，后面是从第几条到第几条数据
from rest_framework.status import HTTP_400_BAD_REQUEST,HTTP_404_NOT_FOUND
from elasticsearch import Elasticsearch
from config.elasticSearchConfig import *

#创建ES客户端对象，连接ES服务
es = Elasticsearch([{'host':HOST,'port':PORT}])

#第一种分页功能
# class TopicPageNumberPagination(PageNumberPagination):
#     page_size = 2 #默认每页多少条数据
#     page_size_query_param = 'size' #可以设置每页数据的多少，规定参数大小
#     max_page_size = 100  #最大每页多少条数据

#第二种分页功能
class TopicLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 3 #默认每页多少条数据
    max_limit = 100 #最大每页多少条数据

class LabelViewSet(ModelViewSet):  #标签类
    queryset = Label.objects.all()  #查询集  （模型）
    serializer_class = LabelSerializer #序列化器查询结果  (序列化器)
    permission_classes = [IsAuthenticated]  #权限控制  ，此处是登陆权限

    @wrap_permission(TeacherPermission)
    @auto_user
    def create(self, request, *args, **kwargs): #增加
        return ModelViewSet.create(self, request, *args, **kwargs)

    @wrap_permission(ActiveUserPPPermission)
    @auto_user
    def update(self, request, *args, **kwargs): #修改
        return ModelViewSet.update(self, request, *args, **kwargs)

    @wrap_permission(RootPersion)
    def destroy(self, request, *args, **kwargs): #删除,需要管理员权限
        return ModelViewSet.destroy(self, request, *args, **kwargs)

class TopicViewSet(ModelViewSet): #试题
    queryset = Topic.objects.filter(is_delete=False)  #查询集  （模型）
    serializer_class = TopicSerializer  #序列化器查询结果  (序列化器)
    permission_classes = [IsAuthenticated]

    # pagination_class = TopicPageNumberPagination  #分页功能
    pagination_class = TopicLimitOffsetPagination  #分页功能

    def get_queryset(self): #关于查询数据的重写
        if self.action in ['practice','random']: #如果接口是顺序刷题和随机刷题就不能看试卷，不给你试卷题目
            return Topic.objects.filter(Q(is_delete=False) & ~Q(label__name__contains='试卷'))  #学生查看方式
        return self.queryset

    def get_serializer(self, *args, **kwargs):
        if self.action in ['exam','random']: #如果你是考试或者模拟，就不给你答案
            return TopicStudentSerializer(*args, **kwargs)
        return self.serializer_class(*args, **kwargs) #如果不是就正常给你所有的题目

    @wrap_permission(TeacherPermission)
    def list(self, request, *args, **kwargs): #查看题目
        return ModelViewSet.list(self, request, *args, **kwargs)

    @wrap_permission(TeacherPermission)
    def retrieve(self, request, *args, **kwargs): #查询单条数据
        return ModelViewSet.retrieve(self, request, *args, **kwargs)

    @action(methods=['get'],detail=False)
    def practice(self,request): #展示给学生的顺序刷题
        return ModelViewSet.list(self,request)

    @action(methods=['get'],detail=False)
    def random(self,request):  #随机刷题
        #获取随即刷题的题目个数
        try:
            size = int(request.query_params.get('size',3))
        except ValueError:
            return Response(status=HTTP_400_BAD_REQUEST)

        #获取查询集，随机抽取指定的个数
        pool = list(self.get_queryset())
        #判断获取的随机题目个数是否超过题目总数，如果超过就给出提示
        if len(pool) < size:
            return Response({'detail':'超出题目个数，题目总数为%s'%len(pool)})
        #序列化，返回数据
        serializer = self.get_serializer(sample(pool,size),many=True)#sample是从pool试题列表里面抽取size个,many=True表示允许多条数据进行查看

        return Response(serializer.data)

    @action(methods=['get'],detail=True)
    def exam(self,request,pk):  #试卷
        #获取指定标签，获取指定试卷
        labels = Label.objects.filter(id=pk,name__contains='试卷').first()
        #判断有没有这个试卷，没有就404
        if not labels:
            return Response(status=HTTP_404_NOT_FOUND)
        #有试卷就获取这个试卷的所有题目，但不包含答案
        topics = labels.topic_set.filter(is_delete=False) #反向查询没有被逻辑删除的题目，后面加_set
        serializer = self.get_serializer(topics,many=True)
        return Response(serializer.data)

    @action(methods=['put'],detail=False)
    def inites(self,request):  #添加数据库里的试题到ES
        #获取mysql中所有的题目数据
        serializer = self.get_serializer(self.get_queryset(),many=True)  #直接把查询集的数据拿到，因为上面开头已经查询并放到集合里了,然后反序列化，把数据转为json
        data = serializer.data
        #保存插入到es中
        for i in data:
            es.index(index='topic', id=i['id'], body=i) #索引，指定id插入数据，body是数据
        return Response(data)

#es同步搜索功能  创建  修改  删除
    @action(methods=['get'],detail=False)
    def search(self,request):
        #获取前端传入的数据
        data = dict(request.query_params)
        offset = int(data.get('offset',[0])[0])  #分页，从第几页开始，后面表示默认为0
        limit = int(data.get('limit',[3])[0])  #每页展示的数据，后面表示默认为0
        subject = data.get('subject',[""])[0] #前端给的是列表,后面是如果subject没有给值，那么默认为空

        #判断用户身份
        user =request.user  #当前发起请求的用户
        #判断身份，查询是否在老师i分组
        group = Group.objects.filter(name='老师').first()  # 获取老师组
        user_groups = user.groups.all()  # 获取传过来的用户所在的用户组
        work_user = user.is_superuser or group in user_groups  # 此处返回布尔值   （看看是否user_groups包含group）
        # work_user = False

        query = {  #自定义高亮搜索
            "query":{

            },
            "highlight": {
                "pre_tags": "<span class='search_key',style='clolor:red'>",
                "post_tags": "</span>",
                "fields": {
                    "subject": {}
                }
            },
            "from":offset,  #分页
            "size":limit
        }
        if work_user:
            #老师或者用户组
            if subject:
                query["query"]["match"] = {
                    "subject":subject
                }
            else:
                query["query"]["match_all"] = {}
        else:
            #学生
            if subject:
                query["query"] = {
                    "bool":{
                        "must":[{
                            "match":{
                                "subject":subject
                            }
                        }],
                        "must_not":[{    #满足此条件的不允许学生查看
                            "match_phrase":{
                                "label_name":"试卷"
                            }
                        }]
                    }
                }
            else:
                #学生直接查询(没有关键字，全部的)
                query["query"] = {
                    "bool":{
                        "must_not": [{  # 满足此条件的不允许学生查看
                            "match_phrase": {
                                "label_name": "试卷"
                            }
                        }]
                    }
                }

        es_res = es.search(index='topic',body=query)
        count = es_res['hits']['total']['value']
        subject_url = f'subject={subject}&' if subject else '' #如果有关键字的话就品接到下一路径里面去，没有就返回空的字符串
        next = f'{CLIENT}/work/topics/search/?{subject_url}limit={limit}&offset={offset + limit}' if offset+limit<count else None #下一页
        previous = f'{CLIENT}/work/topics/search/?{subject_url}limit={limit}&offset={offset - limit}' if offset>0 else None
        results = [ self.highlight(i) for i in es_res['hits']['hits'] ] #把数据循环放入列表中，因为不接受字典，直接受字符串，然后调用高亮搜索替换方法进行替换
        res = {
            "count":count, #数据量
            "next":next,  #下一页的链接
            "previous":previous,#上一页
            "results":results,#数据
        }
        return Response(res)

    def highlight(self, i):
        if i.get('highlight'):  # 如果有高亮搜索，那么进行替换，因为高亮搜索是多出来一块，要把他跟原来的句子替换
            i['_source']['subject'] = i['highlight']['subject']
        return i['_source']


